import aiohttp
import asyncio
from typing import Any, Dict, Optional, Type, TypeVar
from pydantic import BaseModel
from response_handler import ResponseHandler, ResponseData
from exceptions import RequestError, ResponseError

T = TypeVar("T", bound=BaseModel)

class AsyncHttpClient:
    def __init__(
        self,
        base_url: str = "",
        timeout: int = 30,
        max_retries: int = 3,
        token: Optional[str] = None,
        headers: Optional[Dict[str, str]] = None
    ):
        self.base_url = base_url.rstrip('/')
        self.timeout = aiohttp.ClientTimeout(total=timeout)
        self.max_retries = max_retries
        
        # 默认请求头
        self.default_headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        }
        
        if token:
            self.default_headers['Authorization'] = f'Bearer {token}'
            
        if headers:
            self.default_headers.update(headers)
            
    def set_token(self, token: str) -> None:
        """设置认证token"""
        self.default_headers['Authorization'] = f'Bearer {token}'
        
    def _make_url(self, endpoint: str) -> str:
        """构造完整的URL"""
        if endpoint.startswith(('http://', 'https://')):
            return endpoint
        return f"{self.base_url}/{endpoint.lstrip('/')}"
        
    async def _handle_response(self, response: aiohttp.ClientResponse, data_model: Optional[Type[T]] = None) -> ResponseData:
        """处理响应"""
        try:
            response.raise_for_status()
            response_json = await response.json()
        except aiohttp.ContentTypeError:
            raise ResponseError("响应解析失败")
        except aiohttp.ClientResponseError as e:
            raise RequestError(f"HTTP错误: {str(e)}")
            
        response_data = ResponseHandler.parse_response(response_json, data_model)
        if not ResponseHandler.is_success(response_data):
            raise ResponseError(ResponseHandler.get_error_msg(response_data))
            
        return response_data
        
    async def request(
        self,
        method: str,
        endpoint: str,
        data_model: Optional[Type[T]] = None,
        params: Optional[Dict] = None,
        data: Optional[Dict] = None,
        json: Optional[Dict] = None,
        **kwargs
    ) -> ResponseData:
        """发送请求"""
        url = self._make_url(endpoint)
        
        # 设置重试计数
        retries = 0
        last_exception = None
        
        while retries <= self.max_retries:
            try:
                async with aiohttp.ClientSession(timeout=self.timeout) as session:
                    async with session.request(
                        method=method,
                        url=url,
                        params=params,
                        data=data,
                        json=json,
                        headers=self.default_headers,
                        **kwargs
                    ) as response:
                        return await self._handle_response(response, data_model)
            except (aiohttp.ClientError, asyncio.TimeoutError) as e:
                last_exception = e
                retries += 1
                if retries > self.max_retries:
                    break
                # 指数退避重试
                await asyncio.sleep(0.5 * (2 ** (retries - 1)))
        
        if isinstance(last_exception, asyncio.TimeoutError):
            raise RequestError("请求超时")
        else:
            raise RequestError(f"请求异常: {str(last_exception)}")
            
    async def get(self, endpoint: str, data_model: Optional[Type[T]] = None, params: Optional[Dict] = None, **kwargs) -> ResponseData:
        return await self.request('GET', endpoint, data_model=data_model, params=params, **kwargs)
        
    async def post(self, endpoint: str, data_model: Optional[Type[T]] = None, json: Optional[Dict] = None, data: Optional[Dict] = None, **kwargs) -> ResponseData:
        return await self.request('POST', endpoint, data_model=data_model, json=json, data=data, **kwargs)
        
    async def put(self, endpoint: str, data_model: Optional[Type[T]] = None, json: Optional[Dict] = None, data: Optional[Dict] = None, **kwargs) -> ResponseData:
        return await self.request('PUT', endpoint, data_model=data_model, json=json, data=data, **kwargs)
        
    async def delete(self, endpoint: str, data_model: Optional[Type[T]] = None, **kwargs) -> ResponseData:
        return await self.request('DELETE', endpoint, data_model=data_model, **kwargs) 